import re

class UnknownField(Exception): pass
class UnknownStatus(Exception): pass

class PackageStatus(object):

    _status = ()
    _knownParts = (
        "install", "ok", "installed", "unknown",
        "user"
    )

    _wantTo = None

    def __init__(self, statusParts=("not-installed", "ok")):
        self._status = set()
        for _part in statusParts:
            self.incStatus(_part)

    def incStatus(self, status):
        if status not in self._knownParts:
            raise UnknownStatus(status)
        self._status.add(status)

    def update(self, otherStatus):
        for _el in otherStatus.getStatusParts():
            self.incStatus(_el)

    def getStatusParts(self):
        return tuple(self._status)

    def installed(self):
        return "installed" in self._status or \
            self._status.issuperset(("install", "ok"))

    def isOk(self):
        return self._status.intersection(("ok", "installed", "not-installed"))

    def notInstalled(self):
        return not self.installed()

    def __repr__(self):
        return " ".join(self._status)

    def setWantStatus(self, status):
        assert status in ("upgrade", "install", "delete", None)
        self._wantTo = status

    def getWantStatus(self):
        return self._wantTo

class PackageVersion(object):

    _sep = re.compile(r"\W+")
    _digit = re.compile(r"\d+")
    _alpha = re.compile(r"\D+") # not actually an alpha, but close enough
    _origVers = None
    _versionTuple = None

    def __init__(self, versionString):
        self._origVers = versionString
        _version = []
        for _el in self._sep.split(versionString):
            _version.append(self._parseVersionPart(_el))
        self._versionTuple = tuple(_version)

    def _parseVersionPart(self, part):
        _rv = []
        while part:
            _match = self._digit.match(part)
            if _match:
                _val = part[:_match.end()]
                part = part[_match.end():]
                _rv.append(int(_val))
            _match = self._alpha.match(part)
            if _match:
                _val = part[:_match.end()]
                part = part[_match.end():]
                _rv.append(_val)
        return tuple(_rv)

    def getVersionTuple(self):
        return self._versionTuple

    def __cmp__(self, other):
        return cmp(self.getVersionTuple(), other.getVersionTuple())

    def __str__(self):
        return self._origVers

    def __repr__(self):
        return "<%s:%s>" % (self.__clas__.__name__, self._origVers)

class OpkgPackage(object):

    _knownPkgFields = frozenset((
        "Package",
        "Version",
        "Section",
        "Architecture",
        "Maintainer",
        "MD5Sum",
        "Size",
        "Filename",
        "Source",
        "Description",
        "OE",
        "HomePage",
        "Priority",
        "Recommends",
        "Depends",
        "Provides",
        "Tags",
        "Replaces",
        "Conflicts",
        "Suggests",
        "Status",
        "Installed-Time",
        "Auto-Installed",
    ))

    _values = None
    _upgradable = False
    status = property(lambda s: s._values["Status"])

    def __init__(self, valueDict):
        self._values = valueDict.copy()
        _unknownFields = frozenset(self._values.keys()) - self._knownPkgFields
        if _unknownFields:
            raise UnknownField((_unknownFields, valueDict))
        _statusParts = self._values.get("Status", "").split()
        self._values["Status"] = PackageStatus(_statusParts)
        _version = self._values.get("Version", "")
        self._values["Version"] = PackageVersion(_version)

    def getPackageName(self):
        """ Return package name """
        return self._values["Package"]

    def getDescription(self):
        """ Return package description """
        return self._values.get("Description", "")

    def iterValues(self):
        for _value in self._values.itervalues():
            yield _value

    def getValue(self):
        return self._values.copy()

    def getItem(self, name):
        return self._values[name]

    def update(self, other):
        self.status.update(other.status)
        self._upgradable = self._upgradable or \
            self.getItem("Version") < other.getItem("Version")

    def getFullDescription(self):
        """Return human-readable package description (as plain text)"""
        _hiddenFields = ("md5sum", "source", )
        _txt = u""
        for (_name, _value) in self._values.iteritems():
            _txt += "%s:\n\t%s\n" % (_name, _value)
        return _txt.strip()

# vim: set sts=4 sw=4 et :
